/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 battlefield_window.cpp

	$Header: /resource_editor/battlefield_window.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "battlefield_window.h"

#include <cassert>
#include <memory>
#include <vector>

#include "abstract_bitmap.h"
#include "adventure_map.h"
#include "battlefield.h"
#include "battlefield_terrain_map.h"
#include "combat_actor.h"
#include "combat_area_shader.h"
#include "combat_area_shader_data.h"
#include "combat_context.h"
#include "combat_grid_colors.h"
#include "combat_window.h"
#include "convert_16_bit.h"
#include "draw_terrain.h"
#include "format_string.h"
#include "game_time.h"
#include "idle_processor.h"
#include "memory_bitmap.h"
#include "mouse_event.h"
#include "paint_surface.h"
#include "pixel_24.h"
#include "platform.h"
#include "screen_point.h"
#include "shared_ptr.h"
#include "uncopyable.h"

// -----------------------------------------------------------------------
// Unnamed namespace
// -----------------------------------------------------------------------

namespace
{
	// -----------------------------------------------------------------------
	// -----------------------------------------------------------------------
	int greatest_common_divisor( int first, int second )
	{
		while ( second != 0 )
		{
			int remainder = first % second;

			first = second;
			second = remainder;
		}

		return first;
	}

	// -----------------------------------------------------------------------
	// t_bitmap16_scaler class
	// -----------------------------------------------------------------------
	class t_bitmap16_scaler
	{
	public:
		// Constructor
		t_bitmap16_scaler( t_abstract_bitmap16 const & src, t_screen_point const & dest_size );

		// Operator
		std::auto_ptr< t_abstract_bitmap16 > operator()();

	private:
		// Types
		struct t_work_pixel
		{
			t_uint32	red;
			t_uint32	green;
			t_uint32	blue;

			t_work_pixel();
		};

		// Data member
		t_abstract_bitmap16 const &	m_src;
		t_screen_point const &		m_dest_size;
		t_screen_point				m_src_size;
		t_screen_point				m_size_gcd;
		t_screen_point				m_src_pixel_size;
		t_screen_point				m_dest_pixel_size;
		int							m_dest_pixel_area;
		t_screen_point				m_size_lcm;
		std::vector< t_work_pixel >	m_row_accumulation_buffer;

		// Member functions
		void accumulate_src_row( t_abstract_bitmap16::t_pixel const * src_line_ptr, int overlap_height );
		void fill_dest_row( t_abstract_bitmap16::t_pixel * dest_line_ptr );
	};

	// -----------------------------------------------------------------------
	// -----------------------------------------------------------------------
	inline t_bitmap16_scaler::t_work_pixel::t_work_pixel()
		:	red( 0 ),
			green( 0 ),
			blue( 0 )
	{
	}

	// -----------------------------------------------------------------------
	// -----------------------------------------------------------------------
	t_bitmap16_scaler::t_bitmap16_scaler( t_abstract_bitmap16 const & src, t_screen_point const & dest_size )
		:	m_src( src ),
			m_dest_size( dest_size ),
			m_src_size( m_src.get_width(), m_src.get_height() ),
			m_size_gcd(
				greatest_common_divisor( m_src_size.x, m_dest_size.x ),
				greatest_common_divisor( m_src_size.y, m_dest_size.y ) ),
			m_src_pixel_size( m_dest_size.x / m_size_gcd.x, m_dest_size.y / m_size_gcd.y ),
			m_dest_pixel_size( m_src_size.x / m_size_gcd.x, m_src_size.y / m_size_gcd.y ),
			m_dest_pixel_area( m_dest_pixel_size.x * m_dest_pixel_size.y ),
			m_size_lcm( m_src_pixel_size.x * m_src_size.x, m_src_pixel_size.y * m_src_size.y ),
			m_row_accumulation_buffer( m_dest_size.x )
	{
		assert( m_dest_size.x > 0 && m_dest_size.y > 0 );
		assert( m_src_size.x > 0 && m_src_size.y > 0 );
		assert( m_size_lcm.x == m_dest_pixel_size.x * m_dest_size.x );
		assert( m_size_lcm.y == m_dest_pixel_size.y * m_dest_size.y );
	}

	// -----------------------------------------------------------------------
	// -----------------------------------------------------------------------
	std::auto_ptr< t_abstract_bitmap16 > t_bitmap16_scaler::operator()()
	{
		std::auto_ptr< t_abstract_bitmap16 > dest_ptr( new t_memory_bitmap16( m_dest_size.x, m_dest_size.y ) );

		t_abstract_bitmap16::t_pixel * dest_line_ptr = dest_ptr->get_data_ptr();
		int dest_y = 0;

		t_abstract_bitmap16::t_pixel const * src_line_ptr;
		int src_y;
		int next_src_y;
		for (	src_y = 0, src_line_ptr = m_src.get_data_ptr();
				src_y < m_size_lcm.y;
				src_y = next_src_y, src_line_ptr = m_src.advance_line( src_line_ptr ) )
		{
			next_src_y = src_y + m_src_pixel_size.y;
			while ( next_src_y > dest_y + m_dest_pixel_size.y )
			{
				dest_y += m_dest_pixel_size.y;
				int overlap_height = dest_y - src_y;
				src_y = dest_y;

				accumulate_src_row( src_line_ptr, overlap_height );
				fill_dest_row( dest_line_ptr );
				
				dest_line_ptr = dest_ptr->advance_line( dest_line_ptr );
			}

			int overlap_height = next_src_y - src_y;

			accumulate_src_row( src_line_ptr, overlap_height );
		}

		fill_dest_row( dest_line_ptr );

		return dest_ptr;
	}

	// -----------------------------------------------------------------------
	// -----------------------------------------------------------------------
	void t_bitmap16_scaler::accumulate_src_row( t_abstract_bitmap16::t_pixel const * src_line_ptr, int overlap_height )
	{
		int dest_x = 0;
		std::vector< t_work_pixel >::iterator pixel_accumulation_buffer_iter = m_row_accumulation_buffer.begin();

		t_abstract_bitmap16::t_pixel const * src_ptr;
		int src_x;
		int next_src_x;
		for (	src_x = 0, src_ptr = src_line_ptr;
				src_x < m_size_lcm.x;
				src_x = next_src_x, ++src_ptr )
		{
			next_src_x = src_x + m_src_pixel_size.x;

			t_uint32 red_times_height = red_channel( *src_ptr ) * overlap_height;
			t_uint32 green_times_height = green_channel( *src_ptr ) * overlap_height;
			t_uint32 blue_times_height = blue_channel( *src_ptr ) * overlap_height;

			while ( next_src_x > dest_x + m_dest_pixel_size.x )
			{
				t_work_pixel & pixel_accumulation_buffer = *pixel_accumulation_buffer_iter;

				dest_x += m_dest_pixel_size.x;
				int overlap_width = dest_x - src_x;
				src_x = dest_x;

				pixel_accumulation_buffer.red += red_times_height * overlap_width;
				pixel_accumulation_buffer.green += green_times_height * overlap_width;
				pixel_accumulation_buffer.blue += blue_times_height * overlap_width;

				++pixel_accumulation_buffer_iter;
			}

			t_work_pixel & pixel_accumulation_buffer = *pixel_accumulation_buffer_iter;

			int overlap_width = next_src_x - src_x;

			pixel_accumulation_buffer.red += red_times_height * overlap_width;
			pixel_accumulation_buffer.green += green_times_height * overlap_width;
			pixel_accumulation_buffer.blue += blue_times_height * overlap_width;
		}
	}

	// -----------------------------------------------------------------------
	// -----------------------------------------------------------------------
	void t_bitmap16_scaler::fill_dest_row( t_abstract_bitmap16::t_pixel * dest_line_ptr )
	{
		t_abstract_bitmap16::t_pixel * dest_pixel_ptr = dest_line_ptr;
		std::vector< t_work_pixel >::iterator pixel_accumulation_buffer_iter = m_row_accumulation_buffer.begin();
		for (	;
				pixel_accumulation_buffer_iter != m_row_accumulation_buffer.end();
				++pixel_accumulation_buffer_iter )
		{
			t_work_pixel & pixel_accumulation_buffer = *pixel_accumulation_buffer_iter;

			t_uint8 red = pixel_accumulation_buffer.red / m_dest_pixel_area;
			t_uint8 green = pixel_accumulation_buffer.green / m_dest_pixel_area;
			t_uint8 blue = pixel_accumulation_buffer.blue / m_dest_pixel_area;

			*dest_pixel_ptr++ = convert_to_16_bit( t_pixel_24( red, green, blue ) );

			pixel_accumulation_buffer.red = 0;
			pixel_accumulation_buffer.green = 0;
			pixel_accumulation_buffer.blue = 0;
		}
	}

	// -----------------------------------------------------------------------
	// -----------------------------------------------------------------------
	inline std::auto_ptr< t_abstract_bitmap16 > scale_bitmap16( t_abstract_bitmap16 const & src, t_screen_point const & dest_size )
	{
		t_bitmap16_scaler scaler( src, dest_size );
		return scaler();
	}

} // Unnamed namespace

// -----------------------------------------------------------------------
// t_battlefield_window::t_impl members
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// battlefield view cells members
// -----------------------------------------------------------------------
void t_battle_view_cells::set_size( t_screen_point const& size )
{
	m_size = size;
	resize( size.x * size.y );
}

inline t_combat_object_list& t_battle_view_cells::get( t_screen_point const& point )
{
	return *(begin() + point.x + point.y * m_size.x);
}

inline t_combat_object_list const& t_battle_view_cells::get( t_screen_point const& point ) const
{
	return *(begin() + point.x + point.y * m_size.x);
}

// -----------------------------------------------------------------------
// remove underlay objects and replace draw them to the background
// -----------------------------------------------------------------------
static void remove_underlays( t_battlefield& battlefield, t_abstract_bitmap16& background )
{
	t_combat_object_iterator index = battlefield.objects_begin();
	t_combat_object_iterator end = battlefield.objects_end();
	t_abstract_combat_object* object;
	t_compound_object*			compound_object;
	t_screen_point				point;

	while (index != end)
	{
		object = *index;
		++index;
		if (!object->is_underlay() || object->is_animated())
			continue;
		compound_object = object->get_compound_object();
		if (compound_object != 0)
			while (index != end && index->get()->get_compound_object() == compound_object)
				++index;

		point = object->get_shadow_screen_point();
		object->draw_shadow_to( 0, background, point );
		point = object->get_screen_position();
		object->draw_to( 0, background, point );
		battlefield.remove_object( object );
	}
}

// -----------------------------------------------------------------------
// t_battlefield_window members
// -----------------------------------------------------------------------
static int const k_view_cell_shift = 5;
static int const k_view_cell_size = 1 << k_view_cell_shift;


// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_battlefield_window::t_battlefield_window( t_screen_point const &				point,
								t_window *							parent_ptr,
								t_battlefield_ptr					battlefield_ptr,
								t_battlefield_terrain_map const&	terrain_map,
								t_preset_battle_map_array const& battlefield_preset_map_vector,
								bool								draw_terrain_background )
					:	t_window( t_screen_rect( point, battlefield_ptr->get_view_size() ), 
					              k_no_transparency, parent_ptr )
{
	t_screen_point window_size				= battlefield_ptr->get_view_size();
	t_screen_point terrain_map_view_size	= terrain_map.get_view_size();

	t_screen_rect  source_rect(0,0, terrain_map_view_size.x, terrain_map_view_size.y );
	m_last_update_time = get_time();

	m_battlefield = battlefield_ptr;
	source_rect  -= t_screen_point(0, battlefield_ptr->get_base_height() );

	if ( window_size == terrain_map_view_size )
	{
		m_terrain_bitmap_ptr.reset( new t_memory_bitmap16( window_size.x, window_size.y ) );

		if (draw_terrain_background)
			draw_terrain< t_battlefield_terrain_map const & >(
				terrain_map, 0, source_rect, *m_terrain_bitmap_ptr, t_screen_point( 0, 0 ), false );
		if (!battlefield_preset_map_vector.empty())
			draw_preset_background_onto(battlefield_preset_map_vector,m_terrain_bitmap_ptr.get());
	}
	else
	{	
		t_memory_bitmap16 temp_terrain_bitmap( terrain_map_view_size.x, terrain_map_view_size.y );

		if (draw_terrain_background)
			draw_terrain< t_battlefield_terrain_map const & >(
				terrain_map, 0, source_rect, temp_terrain_bitmap, t_screen_point( 0, 0 ), false );
		if (!battlefield_preset_map_vector.empty())
			draw_preset_background_onto(battlefield_preset_map_vector,&temp_terrain_bitmap);
		
		m_terrain_bitmap_ptr = scale_bitmap16( temp_terrain_bitmap, window_size );
	}

	t_screen_point size;

	size.x = (get_width()  + k_view_cell_size - 1) >> k_view_cell_shift;
	size.y = (get_height() + k_view_cell_size - 1) >> k_view_cell_shift;

	m_shadow_view_cells.set_size( size );
	m_view_cells.set_size( size );

	battlefield_ptr->add_viewer( this );

	m_grid_shader = new t_combat_area_shader( *battlefield_ptr );
	battlefield_ptr->place_object( m_grid_shader.get(), t_map_point_3d( 0, 0, 0 ) );

	t_combat_object_list::iterator index = m_battlefield->objects_begin();
	t_combat_object_list::iterator end   = m_battlefield->objects_end();

	for (; index != end; index++)
		invalidate_object( **index );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_battlefield_window::~t_battlefield_window()
{
	m_battlefield->remove_viewer( this );
}

void t_battlefield_window::remove_underlays()
{
	::remove_underlays( *m_battlefield, *m_terrain_bitmap_ptr );
}

// -----------------------------------------------------------------------
// draw_preset_background_onto : composites preset backgrounds onto a given memory_bitmap.
// -----------------------------------------------------------------------
void t_battlefield_window::draw_preset_background_onto( std::vector<t_battlefield_preset_map_in_game_ptr> const& battlefield_preset_map_vector,
													   t_abstract_bitmap16 * memory_bitmap)
{
	for (int i = 0; i < battlefield_preset_map_vector.size(); i++)
	{
		t_bitmap_layer  & backdrop = battlefield_preset_map_vector[i]->get_backdrop();
		backdrop.draw_to( *memory_bitmap,  t_screen_point(0,0) );
	}
}

// -----------------------------------------------------------------------
// compute view rectangle from screen rect
// -----------------------------------------------------------------------
static void compute_view_rect( t_screen_rect const& rect, t_screen_rect& view_rect,
							   t_screen_point const& view_size )
{
	view_rect.left   = rect.left >> k_view_cell_shift;
	view_rect.top	 = rect.top >> k_view_cell_shift;
	view_rect.right  = (rect.right + k_view_cell_size - 1) >> k_view_cell_shift;
	view_rect.bottom = (rect.bottom + k_view_cell_size - 1) >> k_view_cell_shift;
	if (view_rect.left < 0)
		view_rect.left = 0;
	if (view_rect.top < 0)
		view_rect.top = 0;
	if (view_rect.right > view_size.x)
		view_rect.right = view_size.x;
	if (view_rect.bottom > view_size.y)
		view_rect.bottom = view_size.y;
}

// -----------------------------------------------------------------------
// determine correct draw order
// -----------------------------------------------------------------------

// -----------------------------------------------------------------------
// insert an object into a list in correct draw order
// -----------------------------------------------------------------------
static void insert_in_draw_order( t_battlefield& battlefield, t_abstract_combat_object* object,
								  t_combat_object_list& list )
{
	if (object->get_list_position()._Mynode() == NULL)
		return;

	t_combat_object_list::iterator index;
	t_combat_object_list::iterator end = list.end();
	t_combat_object_list::iterator list_position;
	t_combat_object_list::iterator other_position;
	t_combat_object_list::iterator objects_end;
	int							   depth;
	int							   other_depth;
	t_abstract_combat_object*      other;

	depth = object->get_depth();
	for (index = list.begin(); index != end; index++)
	{
		other = index->get();
		other_depth = other->get_depth();
		if (other_depth < depth)
			break;
		if (other_depth > depth)
			continue;
		list_position = object->get_list_position();
		other_position = other->get_list_position();
		if (other_position._Mynode() == NULL)
			continue;
		objects_end = battlefield.objects_end();
		list_position++;
		while ( list_position != other_position && list_position != objects_end )
		{
			other = list_position->get();
			other_depth = other->get_depth();
			if (other_depth < depth)
				break;
			list_position++;
		}
		if (list_position == other_position)
			break;
	}
	list.insert( index, object );
}

// -----------------------------------------------------------------------
// update size of view rectangle
// -----------------------------------------------------------------------
static void update_view_rect( t_battlefield&		battlefield,
							  t_screen_rect const&	view_rect, 
							  t_combat_object_ptr	object, 
							  t_screen_rect const&	old_view_rect,
							  t_battle_view_cells&	view_cells )
{
	if (view_rect != old_view_rect )
	{
		t_screen_point point;

		for (point.y = old_view_rect.top; point.y < old_view_rect.bottom; point.y++)
		{
			for (point.x = old_view_rect.left; point.x < old_view_rect.right; point.x++)
			{
				view_cells.get(point).remove( object );
			}
		}
		for (point.y = view_rect.top; point.y < view_rect.bottom; point.y++)
		{
			for (point.x = view_rect.left; point.x < view_rect.right; point.x++)
			{
				insert_in_draw_order( battlefield, object, view_cells.get(point) );
			}
		}
	}
}

// -----------------------------------------------------------------------
// invalidate an combat object an update the view cells
// -----------------------------------------------------------------------
void t_battlefield_window::invalidate_object( t_abstract_combat_object&	object )
{
	t_screen_point	base_point;
	t_screen_rect	invalid_rect;
	t_screen_rect   view_rect;

	base_point = object.get_shadow_screen_point();
	invalid_rect = object.get_shadow_rect( 0 ) + base_point;
	invalidate( invalid_rect );
	if (object.get_alpha() == 0 || object.get_list_position()._Mynode() == NULL )
		view_rect = t_screen_rect(0,0,0,0);
	else
		compute_view_rect( invalid_rect, view_rect, m_view_cells.get_size() );
	update_view_rect( *m_battlefield, view_rect, &object, object.get_shadow_view_rect(), 
		              m_shadow_view_cells );
	object.set_shadow_view_rect( view_rect );
	base_point = object.get_screen_position();
	invalid_rect = object.get_rect( 0 ) + base_point;
	invalidate( invalid_rect );
	if (object.get_alpha() == 0 || object.get_list_position()._Mynode() == NULL )
		view_rect = t_screen_rect(0,0,0,0);
	else
		compute_view_rect( invalid_rect, view_rect, m_view_cells.get_size() );
	update_view_rect( *m_battlefield, view_rect, &object, object.get_view_rect(), m_view_cells );
	object.set_view_rect( view_rect );
}


// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
std::string t_battlefield_window::get_help_text( t_screen_point point ) const
{
	return m_battlefield->get_help_text();
}

// -----------------------------------------------------------------------
// add objects to draw list
// -----------------------------------------------------------------------
static void create_draw_list( t_battlefield&				battlefield,
							  t_screen_rect const&			view_rect,
							  t_battle_view_cells const&	view_cells,
							  t_combat_object_list&			draw_list )
{
	t_combat_object_list::const_reverse_iterator index;
	t_combat_object_list::const_reverse_iterator end;
	t_abstract_combat_object*					 object;
	t_screen_point								 point;

	for (point.y = view_rect.top; point.y < view_rect.bottom; point.y++)
	{
		for (point.x = view_rect.left; point.x < view_rect.right; point.x++)
		{
			t_combat_object_list const& objects = view_cells.get( point );

			end = objects.rend();
			for (index = objects.rbegin(); index != end; index++)
			{
				object = index->get();
				if (!object->is_on_draw_list() && object->get_list_position()._Mynode() != NULL)
				{
					insert_in_draw_order( battlefield, object, draw_list );
					object->set_on_draw_list( true );
				}
			}
		}
	}
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_battlefield_window::paint( t_paint_surface & paint_surface )
{
	// First paint the terrain
	paint_surface.draw( *m_terrain_bitmap_ptr );

	t_screen_rect			paint_rect = paint_surface.get_rect();
	t_abstract_bitmap16_ptr paint_bitmap_ptr = paint_surface.get_bitmap();

	// create shadow draw list
	t_combat_object_list			shadow_draw_list;
	t_combat_object_list			draw_list;
	t_screen_rect					view_rect;
	t_screen_point					point;
	t_combat_object_const_iterator	index;
	t_combat_object_const_iterator	end;
	t_abstract_combat_object*		object;

	compute_view_rect( paint_rect, view_rect, m_view_cells.get_size() );
	create_draw_list( *m_battlefield, view_rect, m_shadow_view_cells, shadow_draw_list );

	end = shadow_draw_list.end();
	for (index = shadow_draw_list.begin(); index != end; index++)
	{
		object = index->get();
		if (object->is_elevated())
		{
			insert_in_draw_order( *m_battlefield, object, draw_list );
		}
		else
		{
			point = object->get_shadow_screen_point();
			object->draw_shadow_to( m_last_update_time, *paint_bitmap_ptr, 
									point - paint_rect.top_left() );
			object->set_on_draw_list( false );
		}
	}

	create_draw_list( *m_battlefield, view_rect, m_view_cells, draw_list );
	end = draw_list.end();
	for (index = draw_list.begin(); index != end; index++)
	{
		object = index->get();
		if (object->is_elevated())
		{
			point = object->get_shadow_screen_point();
			object->draw_shadow_to( m_last_update_time, *paint_bitmap_ptr, 
									point - paint_rect.top_left() );
		}
		point = object->get_screen_position();
		object->draw_to( m_last_update_time, *paint_bitmap_ptr, 
			                    point - paint_rect.top_left() );
		object->set_on_draw_list( false );
	}
	draw_list.clear();
}

// -----------------------------------------------------------------------
// handle a left-button down
// -----------------------------------------------------------------------
void t_battlefield_window::left_button_down( t_mouse_event const& event )
{
	m_battlefield->left_button_down( event.client_point );
}

// -----------------------------------------------------------------------
// handle a left-button up 
// -----------------------------------------------------------------------
void t_battlefield_window::left_button_up( t_mouse_event const& event )
{
	m_battlefield->left_click( event.client_point );
}

// -----------------------------------------------------------------------
// handle a right-button up 
// -----------------------------------------------------------------------
void t_battlefield_window::right_button_up( t_mouse_event const& event )
{
	m_battlefield->right_click( event.client_point );
}

// -----------------------------------------------------------------------
// handle a left-button up 
// -----------------------------------------------------------------------
void t_battlefield_window::mouse_move( t_mouse_event const& event )
{
	if ( get_mouse_position( this ) != event.client_point )
		return;
	update_cursor( event.client_point );
}

// ------------------------------------------------------------------------
// determine correct cursor to display
// ------------------------------------------------------------------------
void t_battlefield_window::update_cursor()
{
	t_screen_point point = get_mouse_position( this );
	t_screen_rect  rect = get_client_rect();

	if (!is_point_in_rect( point, rect ))
		return;
	update_cursor( point );
}

// ------------------------------------------------------------------------
// determine correct cursor to display
// ------------------------------------------------------------------------
void t_battlefield_window::update_cursor( t_screen_point point )
{
	t_mouse_window* cursor = m_battlefield->get_new_cursor( point );

	if (cursor != get_cursor( false ))
		set_cursor( cursor );
}


t_combat_object_list& t_battlefield_window::get_objects( t_screen_point const& point )
{
	return m_view_cells.get( point >> k_view_cell_shift );
}

// Produce a checkerboard effect on top of the combat map, to show the actual grid
void t_battlefield_window::set_grid_shading( bool arg )
{
	if ( arg )
	{
		if ( m_battlefield == NULL )
			return;
		if ( m_battlefield->get_combat_window() == NULL )
			return;

		t_combat_context_ptr context = m_battlefield->get_combat_window()->get_context();

		if ( context == NULL )
			return;

		// Get the combat grid colors structure
		t_adv_map_point const & location = context->get_location();
		t_adventure_map const & map = *context->get_adventure_map();
		t_adventure_tile const & tile = map.get_adv_tile( location );
		t_terrain_type terrain = tile.get_terrain();

		// Sometimes "sea" battles can happen on land squares; when a ship moves to attack a creature
		// standing on land. Force it to always use the water (a.k.a boat) coloring
		if (m_battlefield->is_sea_battle() )
			terrain = k_terrain_water; 

		t_combat_grid_colors const & grid_colors = get_combat_grid_colors( terrain, location.on_bridge );

		int size = m_battlefield->get_size();
		t_combat_area_shader_data data( size );

		t_uint8 color_indexes[2];
		
		for ( int i = 0; i < ELEMENTS_OF(color_indexes); i++ )
		{
			if ( grid_colors.color[ i ].transparent )
			{
				color_indexes[i] = 0;
			}
			else
			{
				color_indexes[i] = data.add_to_palette( grid_colors.color[i].color );
			}
		}

		t_map_point_2d point;

		for ( point.row = 0; point.row < size; point.row++ )
		{
			for ( point.column = 0; point.column < size; point.column++ )
			{
				if ( m_battlefield->is_valid( point ) )
				{
					t_battlefield_cell const & cell = m_battlefield->get_cell( point );

					if ( !cell.is_forbidden_by_preset() )
					{
						int index = ( point.row + point.column ) & 0x1;

						data.set_tile_to_color( point, color_indexes[ index ] );
					}
				}
			}
		}
		
		m_grid_shader->set_alpha( *m_battlefield, grid_colors.alpha );
		m_grid_shader->set_shading( data, *m_battlefield );
	}
	else
	{
		m_grid_shader->reset( *m_battlefield );
	}
}

